class entity_t {
    constructor (x, y, z, friction, sprite, init_param) {
        let t = this;
        t.x = x;
        t.y = y;
        t.z = z;
        t.vx = t.vy = t.vz = t.ax = t.ay = t.az = 0;
        t.f = friction;
        t.s = sprite;
        t.h = 5;

        t._init (init_param);
        entities.push (t);
    }

    // separate _init() method, because "constructor" cannot be uglyfied
    _init (init_param) {
    }

    _update () {
        let t = this,
            last_x = t.x, last_z = t.z;

        // velocity
        t.vx += t.ax * time_elapsed - t.vx * _math.min (t.f * time_elapsed, 1);
        t.vy += t.ay * time_elapsed - t.vy * _math.min (t.f * time_elapsed, 1);
        t.vz += t.az * time_elapsed - t.vz * _math.min (t.f * time_elapsed, 1);

        // position
        t.x += t.vx * time_elapsed;
        t.y += t.vy * time_elapsed;
        t.z += t.vz * time_elapsed;

        // check wall collissions, horizontal
        if (t._collides (t.x, last_z)) {
            t._did_collide (t.x, t.y);
            t.x = last_x;
            t.vx = 0;
        }

        // check wall collissions, vertical
        if (t._collides (t.x, t.z)) {
            t._did_collide (t.x, t.y);
            t.z = last_z;
            t.vz = 0;
        }
    }

    _collides (x, z) {
        return level_data[(x >> 3) + (z >> 3) * level_width] > 7 || // top left
            level_data[((x + 6) >> 3) + (z >> 3) * level_width] > 7 || // top right
            level_data[((x + 6) >> 3) + ((z + 4) >> 3) * level_width] > 7 || // bottom right
            level_data[(x >> 3) + ((z + 4) >> 3) * level_width] > 7; // bottom left
    }

    _spawn_particles (amount) {
        for (let i = 0; i < amount; i++) {
            let particle = new entity_particle_t (this.x, 0, this.z, 1, 30);
            particle.vx = (_math.random () - 0.5) * 128;
            particle.vy = _math.random () * 96;
            particle.vz = (_math.random () - 0.5) * 128;
        }
    }

    // collision against static walls
    _did_collide () {
    }

    // collision against other entities
    _check (other) {
    }

    _receive_damage (from, amount) {
        this.h -= amount;
        if (this.h <= 0) {
            this._kill ();
        }
    }

    _kill () {
        if (!this._dead) {
            this._dead = true;
            entities_to_kill.push (this);
        }
    }

    _render () { // render
        let t = this;
        push_sprite (t.x - 1, t.y, t.z, t.s);
    }
}
